package cn.virens.server.netty_udp;

import java.util.function.Consumer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.hutool.core.thread.ThreadUtil;
import cn.virens.app.AppConfig;
import cn.virens.app.cmpt.Component;
import cn.virens.server.netty_udp.handler.PacketHandler;
import cn.virens.server.netty_udp.packet.UdpWritePacket;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup;

public class UDPServerManagerImpl implements UDPServerManager, Component {
	private final Logger logger = LoggerFactory.getLogger(UDPServerManagerImpl.class);

	private final EventLoopGroup mEventLoopGroup = new NioEventLoopGroup();
	private final Bootstrap mBootstrap = new Bootstrap();

	private final UDPServerPacketHandler serverHandler = new UDPServerPacketHandler();
	private final UDPServerPacketDecoder decoder = new UDPServerPacketDecoder(serverHandler);
	private final UDPServerPacketEncoder encoder = new UDPServerPacketEncoder(serverHandler);

	private final EventExecutorGroup mExecutorGroup = new DefaultEventExecutorGroup(2);

	private ChannelFuture channelFuture;
	private Integer port;

	@Override
	public void initialize(AppConfig config) throws Exception {
		ThreadUtil.newThread(() -> {
			try {
				this.mBootstrap.group(mEventLoopGroup);
				this.mBootstrap.channel(NioDatagramChannel.class);
				this.mBootstrap.option(ChannelOption.SO_BROADCAST, true);
				this.mBootstrap.handler(new ChannelInitializer<NioDatagramChannel>() {

					@Override
					protected void initChannel(NioDatagramChannel ch) throws Exception {
						ch.pipeline().addLast("datagramPacketDecoder", decoder);
						ch.pipeline().addLast("datagramPacketEncoder", encoder);
						ch.pipeline().addLast(mExecutorGroup, "handler", serverHandler);
					}
				});

				this.port = config.getInteger("netty.udp.port");
				this.channelFuture = mBootstrap.bind(port).sync();
				this.channelFuture.channel().closeFuture().await();
			} catch (Exception e) {
				logger.error("启动失败:" + e.getMessage(), e);
			} finally {
				logger.debug("Netty UDP Server is stoped!");
			}
		}, "Netty UDP Server Thread", true).start();
	}

	@Override
	public void destroy() throws Exception {
		logger.debug("The UDP Server Manager stoping...");

		this.mExecutorGroup.shutdownGracefully();
		this.mEventLoopGroup.shutdownGracefully();
		this.channelFuture.channel().close().sync();
	}

	@Override
	public UDPServerChannel poolRm(String mac) {
		return serverHandler.poolRm(mac);
	}

	@Override
	public UDPServerChannel poolGet(String mac) {
		return serverHandler.poolGet(mac);
	}

	@Override
	public void poolEach(Consumer<UDPServerChannel> action) {
		this.serverHandler.poolEach(action);
	}

	public void addHandler(PacketHandler handler) {
		this.serverHandler.addHandler(handler);
	}

	@Override
	public boolean write(UdpWritePacket packet) {
		return serverHandler.write(packet);
	}
}
